home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / idle / autolog-.000 / autolog- / autolog-0.31 / autolog.c next >
C/C++ Source or Header  |  1995-08-19  |  16KB  |  433 lines

  1. /*  "@(#) autolog.c      0.30
  2.     Originally ported by David Dickson" 
  3.     Modified by Michael C. Mitchell, 15Oct94
  4.     Rewritten by Kyle Bateman, Nov94, Aug95
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. */
  20.  
  21. #include    <stdio.h>
  22. #include    <signal.h>
  23. #include    <string.h>
  24. #include    <sys/types.h>
  25. #include    <sys/stat.h>
  26. #include    <utmp.h>
  27. #include    <time.h>
  28. #include    <pwd.h>
  29. #include    <grp.h>
  30.  
  31. #define     D_IDLE      30  /* maximum idle time (minutes) */
  32. #define     D_GRACE     30  /* grace time (sec) for user reply */
  33. #define     D_HARD      0   /* consider connect time only */
  34. #define     D_MAIL      1   /* notify user by mail */
  35. #define     D_CLEAR     0   /* clear user terminal before warning */
  36. #define     D_WARN      1   /* warn user before killing */
  37. #define     D_LOG       1   /* log activity in logfile */
  38.  
  39. #define     WARNING     1   /* a warning message */
  40. #define     LOGOFF      2   /* a log-off message */
  41. #define     NOLOGOFF    3   /* a log-off failure message */
  42.  
  43. #define     KWAIT       20  /* time to wait after kill (in sec.) */
  44. #define     STRLEN      64
  45. #define     LINELEN     256
  46. #define     MAXCONF     512 /* maximum lines in config file */
  47.  
  48. struct utmp *utmpp;         /* pointer to utmp file entry */
  49. char delims[] = "\n\t ";    /* valid delimiters in config file */
  50. char anystrg[] = ".*";      /* matches anything */
  51.  
  52. int debug = 0;
  53. int nokill = 0;
  54. int listall = 0;
  55. char *confname = "/etc/autolog.conf";
  56. char *logfname = "/var/adm/autolog";
  57. int g_idle = D_IDLE;
  58. int g_grace = D_GRACE;
  59. int g_hard = D_HARD;
  60. int g_mail = D_MAIL;
  61. int g_clear = D_CLEAR;
  62. int g_warn = D_WARN;
  63. int g_log = D_LOG;
  64.  
  65. typedef struct conf
  66.     {
  67.     char    *name;          /* user name */
  68.     char    *group;         /* group name */
  69.     char    *line;          /* tty line */
  70.     int     idle;           /* minutes of idle time */
  71.     int     grace;          /* minutes of grace time */
  72.     int     hard;           /* consider connect time only */
  73.     int     mail;           /* notify by mail */
  74.     int     clear;          /* do screen clears before warn */
  75.     int     warn;           /* warn before kill */
  76.     int     log;            /* log actions to logfile */
  77.     } conf_el;
  78. conf_el c_arr[MAXCONF];
  79. int c_idx = 0;
  80.  
  81. main(int argc, char *argv[])
  82.     {
  83.     int i;
  84.     for (i = 1; i < argc; i++)
  85.         if (argv[i][0] == '-')
  86.             switch(argv[i][1])
  87.                 {
  88.                 case 'a':
  89.                     listall = 1;
  90.                     break;
  91.                 case 'd':
  92.                     debug = 1;
  93.                     break;
  94.                 case 'n':
  95.                     nokill = 1;
  96.                     break;
  97.                 case 'h':
  98.                     g_hard = 1;
  99.                     break;
  100.                 case 'l':
  101.                     logfname = argv[++i];
  102.                     break;
  103.                 case 'f':
  104.                     confname = argv[++i];
  105.                     break;
  106.                 case 't':
  107.                     g_idle = atoi(argv[++i]);
  108.                     break;
  109.                 case 'g':
  110.                     g_grace = atoi(argv[++i]);
  111.                     break;
  112.                 case 'm':
  113.                     g_mail = (argv[++i][0] == 'y');
  114.                     break;
  115.                 case 'c':
  116.                     g_clear = (argv[++i][0] == 'y');
  117.                     break;
  118.                 case 'w':
  119.                     g_warn = (argv[++i][0] == 'y');
  120.                     break;
  121.                 case 'L':
  122.                     g_log = (argv[++i][0] == 'y');
  123.                     break;
  124.                 default:
  125.                     fprintf(stderr,"autologout: illegal switch: %s\n", argv[i]);
  126.                 }
  127.         else
  128.             fprintf(stderr,"autologout: illegal parameter: %s\n", argv[i]);
  129.     eat_confile();              /* read config file */
  130.     if (!debug)                 /* if not in debug mode, */
  131.         if (fork())             /* the parent process */
  132.             exit(0);            /* exits */
  133.  
  134.     /* the child processes all utmp file entries: */
  135.     while ((utmpp = getutent()) != (struct utmp *) NULL)
  136.         check_idle();
  137.     exit(0);                    /* done, so bye */
  138.     }
  139.  
  140. set_defs(int i)
  141.     {
  142.     c_arr[i].name = anystrg;
  143.     c_arr[i].group = anystrg;
  144.     c_arr[i].line = anystrg;
  145.     c_arr[i].idle = g_idle;
  146.     c_arr[i].grace = g_grace;
  147.     c_arr[i].hard = g_hard;
  148.     c_arr[i].mail = g_mail;
  149.     c_arr[i].clear = g_clear;
  150.     c_arr[i].warn = g_warn;
  151.     c_arr[i].log = g_log;
  152.     }
  153.  
  154. eat_confile()
  155.     {
  156.     FILE *f;
  157.     char *s, iline[LINELEN];
  158.     int i, lev;
  159.     
  160.     if (!(f=fopen(confname, "r")) )
  161.         {
  162.         if (debug)
  163.             printf("Can't find file: %s\n", confname);
  164.         }
  165.     else
  166.         {
  167.         while (fgets(iline, LINELEN, f))
  168.             {
  169.             if (*iline == '#' || strlen(iline) <= 1)
  170.                 continue;
  171.             set_defs(c_idx);
  172.             s=strtok(iline,delims);
  173.             do
  174.                 {
  175.                 lev = 1;
  176.                 if (!strncmp(s,"name=",5) && *(s+=5))
  177.                     c_arr[c_idx].name=strcpy((char *)malloc(strlen(s)+1),s);
  178.                 else if (!strncmp(s,"group=",6) && *(s+=6))
  179.                     c_arr[c_idx].group=strcpy((char *)malloc(strlen(s)+1),s);
  180.                 else if (!strncmp(s,"line=",5) && *(s+=5))
  181.                     c_arr[c_idx].line=strcpy((char *)malloc(strlen(s)+1),s);
  182.                 else if (!strncmp(s,"idle=",5) && *(s+=5))
  183.                     c_arr[c_idx].idle=atoi(s);
  184.                 else if (!strncmp(s,"grace=",6) && *(s+=6))
  185.                     c_arr[c_idx].grace=atoi(s);
  186.                 else
  187.                     {
  188.                     if (!strncmp(s,"no",2))
  189.                         {
  190.                         lev=0;
  191.                         s+=2;
  192.                         }
  193.                     if (!strcmp(s,"hard"))
  194.                         c_arr[c_idx].hard=lev;
  195.                     else if (!strcmp(s,"clear"))
  196.                         c_arr[c_idx].clear=lev;
  197.                     else if (!strcmp(s,"mail"))
  198.                         c_arr[c_idx].mail=lev;
  199.                     else if (!strcmp(s,"warn"))
  200.                         c_arr[c_idx].warn=lev;
  201.                     else if (!strcmp(s,"log"))
  202.                         c_arr[c_idx].log=lev;
  203.                     else if (debug)
  204.                         printf("Unknown token in file: %s: %s\n",confname,s);
  205.                     }
  206.                 }
  207.             while(s=strtok(0,delims));
  208.             c_idx++;
  209.             }
  210.         fclose(f);
  211.         }
  212.     if (!c_idx)                 /* if no entries made yet */
  213.         set_defs(c_idx++);      /* make one */
  214.     if (debug)
  215.         for(i=0;i < c_idx; i++)
  216.             printf("name=%s group=%s line=%s idle=%d grace=%d mail=%d warn=%d log=%d\n",
  217.                 c_arr[i].name,c_arr[i].group,c_arr[i].line,c_arr[i].idle,
  218.                 c_arr[i].grace,c_arr[i].mail,c_arr[i].warn,c_arr[i].log);
  219.     }
  220.  
  221. /* return true if strg matches the regex in pattern */
  222. pat_match(char *pattern,char *strg)
  223.     {
  224.     re_comp(pattern);
  225.     return(re_exec(strg));
  226.     }
  227.  
  228. check_idle()            /* select utmp entries needing killing */
  229.     {
  230.     char dev[STRLEN], name[STRLEN], prname[STRLEN], *gn = "";
  231.     struct stat status;
  232.     time_t idle, pres_time, start, time(), stime;
  233.     struct passwd *passwd_entry;
  234.     struct group *group_entry;
  235.     conf_el *ce;
  236.     int i;
  237.  
  238.     if (utmpp->ut_type != USER_PROCESS)     /* if not a user process */
  239.         {
  240.         if (listall)
  241.             printf("Non-user process: N:%-8s P:%5d Login:%s",utmpp->ut_user,utmpp->ut_pid,ctime(&utmpp->ut_time));
  242.         return(0);                          /* skip the utmp entry */
  243.         }
  244.     sprintf(dev,"/dev/%s",utmpp->ut_line);  /* append /dev/ to base name */
  245.     if (stat(dev, &status))                 /* if can't get status for port */
  246.         bailout("Can't get status of user's terminal", 1);
  247.  
  248.     /* idle time = current time less last access time: */
  249.     time(&pres_time);                           /* get current time */
  250.     idle = (pres_time - status.st_atime) / 60;  /* total idle minutes */
  251.     stime = (pres_time - utmpp->ut_time) / 60;  /* total session minutes */
  252.     strncpy(name, utmpp->ut_user, UT_NAMESIZE); /* get user name */
  253.     name[UT_NAMESIZE] = '\0';           /* null terminate user name string */
  254.     if (debug)
  255.         printf("\nChecking: %-11s on %-12s I:%-4d Login: %s",name,dev,idle,ctime(&utmpp->ut_time));
  256.  
  257.     /* now try to find the group of this person */
  258.     /* if usernames in utmp are limited to 8 chars, we will may fail on */
  259.     /* names that are longer than this, so we'll try to find it by uid */
  260.     if (!(passwd_entry = getpwnam(name)))       /* If can't find by name */
  261.         passwd_entry = getpwuid(status.st_uid); /* try by uid */
  262.     if (passwd_entry)
  263.         {
  264.         strcpy(name,passwd_entry->pw_name);
  265.         if(group_entry = getgrgid( passwd_entry->pw_gid ))
  266.             gn = group_entry->gr_name;
  267.         else if (debug)
  268.             printf("Can't find group entry for user: %s\n",name);
  269.         }
  270.     else if (debug)
  271.         printf("Can't find password entry for user: %s\n",name);
  272.     
  273.     for(i = 0; i < c_idx; i++)
  274.         if (pat_match(c_arr[i].name,name) && 
  275.             pat_match(c_arr[i].group,gn) &&
  276.             pat_match(c_arr[i].line,utmpp->ut_line))
  277.             {
  278.             if (debug)
  279.                 printf("Match #%2d: U:%-12s Grp:%-8s Line:%-9s Pid:%-6d Sess:%3d:%02d\n",
  280.                     i+1,name,gn,utmpp->ut_line,utmpp->ut_pid,stime/60,stime%60);
  281.             if (!c_arr[i].idle)     /* if user exempt (idle=0) */
  282.                 {
  283.                 if (debug)
  284.                     printf("User exempt\n");
  285.                 return(0);          /* then don't kill him */
  286.                 }
  287.             ce = &c_arr[i];         /* get address of matched record */
  288.             break;
  289.             }
  290.     if (i >= c_idx)
  291.         {
  292.         if (debug)
  293.             printf("No match for process\n");
  294.         return(0);
  295.         }
  296.         
  297.     sprintf(prname,"/proc/%d",utmpp->ut_pid);   /* make filename to check */
  298.     if (stat(prname, &status))              /* is this a valid utmp entry? */
  299.         {
  300.         if (debug)
  301.             printf("Process %d not found in process table\n",utmpp->ut_pid);
  302.         return(0);
  303.         }
  304.     if (!c_arr[i].hard)                 /* if considering idle time */
  305.         {
  306.         if (debug)
  307.             printf("Subject to logout   Idle time: %4d (%2d allowed)\n",idle,ce->idle);
  308.         if (idle < ce->idle)    /* if user was recently active */
  309.             return(0);                  /* let it live */
  310.         }
  311.     else
  312.         {
  313.         if (debug)
  314.             printf("Subject to logout   Total time: %4d (%2d allowed)\n",stime,ce->idle);
  315.         if (stime < ce->idle)   /* if user still under limit */
  316.             return(0);                  /* let it live */
  317.         }
  318.     if (nokill)
  319.         {
  320.         if (debug)
  321.             printf("Would kill this process\n");
  322.         return(1);
  323.         }
  324.     if (debug)
  325.         printf("Warning user:%s Line:%s  Sleep %d sec\n",name,utmpp->ut_line,ce->grace);
  326.     mesg(WARNING, name, dev, stime, idle, ce); /* send warning to user */
  327.     if (stat(dev, &status))
  328.         bailout("Can't get status of user's terminal", 2);
  329.     start = status.st_atime;                /* start time for countdown */
  330.     sleep(ce->grace);
  331.     if (stat(dev, &status))
  332.         bailout("Can't get status of user's terminal", 3);
  333.     if (ce->hard || start >= status.st_atime)   /* user still idle */
  334.         {
  335.         if (debug)
  336.             printf("Killing user:%s Line:%s  Sleep %d sec\n",name,utmpp->ut_line,KWAIT);
  337.         if (killit(utmpp->ut_pid))
  338.             {
  339.             mesg(LOGOFF, name, dev, stime, idle, ce); /* send warning to user */
  340.             return(1);
  341.             }
  342.         mesg(NOLOGOFF, name, dev, stime, idle, ce); /* couldn't kill */
  343.         }
  344.     return(0);
  345.     }
  346.  
  347. mesg(int flag, char *name, char *dev, int stime, int idle, conf_el *ce)
  348.     {
  349.     char    mbuf[LINELEN];          /* message buffer */
  350.     time_t  tvec;
  351.     FILE    *fp, *log = 0, *mprog;
  352.     struct stat status;
  353.  
  354.     time(&tvec);                /* tvec = current time */
  355.     if (stat(logfname, &status) >= 0)           /* if logfile exists */
  356.         log = fopen(logfname, "a"); /* append to it */
  357.     if (ce->clear) 
  358.         {
  359.         sprintf (mbuf,"clear >%s",dev);
  360.         system (mbuf);
  361.         }
  362.     if (flag == WARNING && ce->warn)
  363.         {                       /* process warning message */
  364.         if (!(fp = fopen(dev, "w")) )
  365.             bailout("Can't open user's terminal", 5);
  366.         if (ce->hard)
  367.             {
  368.             fprintf(fp,"%s: You've been on for %d min.\07\n", name, stime);
  369.             fprintf(fp,"You'll be logged off in %d sec. so finish up:",ce->grace);
  370.             }
  371.         else
  372.             {
  373.             fprintf(fp,"%s: You've been idle for %d min.\07\n", name, idle);
  374.             fprintf(fp,"You'll be logged off in %d sec unless you hit a key",ce->grace);
  375.             }
  376.         fclose(fp);
  377.         if (log && ce->log)
  378.             fprintf(log, "** NOTIFIED ** %s %s idle:%d sess:%s %s",name, dev+5, idle, stime, ctime(&tvec)+3);
  379.         }
  380.     if (flag == LOGOFF) 
  381.         {                       /* process log-off message */
  382.         if (log && ce->log)
  383.             fprintf(log, "** LOGOFF ** %s %s idle:%d sess:%s %s",name, dev+5, idle, stime, ctime(&tvec)+3);
  384.         if (ce->mail)
  385.             {
  386.             sprintf(mbuf, "/bin/mail %s", name);
  387.             /* open pipe to mail program for writing */
  388.             if (!(mprog = popen(mbuf, "w")) )
  389.                 bailout("Can't use /bin/mail program", 6);
  390.             fprintf(mprog, "Subject: Excess Idle Time\nLogged off - excess idle time - %s %s\ntty = %s\n",name, ctime(&tvec), dev+5);
  391.             fclose(mprog);
  392.             }
  393.         }
  394.     if (flag == NOLOGOFF) 
  395.         {                       /* send mail to root if can't kill */
  396.         if (log && ce->log)
  397.             fprintf(log, "** LOGOFF-FAIL ** %s (pid = %d) %s (%d min idle time) %s",name, utmpp->ut_pid, dev+5, idle, ctime(&tvec)+3);
  398.         if (ce->mail)
  399.             {
  400.             sprintf(mbuf, "/bin/mail root");
  401.             if ((mprog = popen(mbuf, "w")) == (FILE *) NULL)
  402.                 bailout("Can't use /bin/mail program", 7);
  403.             fprintf(mprog, "Subject: Can't logoff %s\nCan't Log off - %s %s\ntty = %s\n", name, name, ctime(&tvec), dev+5);
  404.             fclose(mprog);
  405.             }
  406.         }
  407.     fclose(log);
  408.     return(0);
  409.     }
  410.  
  411. killit(int pid)     /* terminate process using SIGHUP, then SIGKILL */
  412.     {
  413.     kill(pid, SIGHUP);          /* first send "hangup" signal */
  414.     sleep(KWAIT);
  415.     if (!kill(pid, 0)) 
  416.         {                       /* SIGHUP might be ignored */
  417.         kill(pid, SIGKILL);     /* then send sure "kill" signal */
  418.         sleep(KWAIT);
  419.         if (!kill(pid, 0))
  420.             return(0);          /* failure--refuses to die! */
  421.         else
  422.             return(1);          /* successful kill with SIGKILL */
  423.         }
  424.     else
  425.         return(1);              /* successful kill with SIGHUP */
  426.     }
  427.  
  428. bailout(char *message, int status)  /* display error message and exit */
  429.     {
  430.     fprintf(stderr,"autologout: %s\n", message);
  431.     exit(status);
  432.     }
  433.